import asyncio
from common.utility import Utility

async def say_after(content: str, delay: int = 1) -> None:
    Utility.print_info(f'entered. It will delay {delay} seconds.')
    await asyncio.sleep(delay)
    Utility.print_info(content)
    Utility.print_info('exited.')

async def asyncio_timeout_error():
    try:
        async with asyncio.timeout(2):
            await say_after('hello', 1)
            await say_after('world', 3)
    except asyncio.TimeoutError as err:
        Utility.print_info(f'caught a timeout error: {err}')

async def asyncio_timeout_class(deadline: int|float, delay: int|float):
    try:
        loop: asyncio.AbstractEventLoop = asyncio.get_running_loop()
        loop_time: float = loop.time()
        Utility.print_info(f'loop time: {loop_time}')
        async with asyncio.timeout(None) as cm:
            Utility.print_info(f'current deadline: {cm.when()}')
            new_deadline: float = loop_time + deadline
            cm.reschedule(new_deadline)
            Utility.print_info(f'new deadline: {cm.when()}')
            await say_after('hello', delay)
    except asyncio.TimeoutError as err:
        Utility.print_info(f'caught a timeout error: {err}')
    Utility.print_info(f'expired: {cm.expired()}')

async def asyncio_timeout_at(when: int|float|None, delay: int|float|None):
    try:
        loop: asyncio.AbstractEventLoop = asyncio.get_running_loop()
        loop_time: float = loop.time()
        Utility.print_info(f'loop time: {loop_time}')
        Utility.print_info('before calling timeout_at function.')
        async with asyncio.timeout_at(loop_time+when):
            Utility.print_info(f'after called timeout_at function. when is {when} seconds.')
            await say_after('hello', delay)
            Utility.print_info(f'after delayed {delay} seconds.')
    except asyncio.TimeoutError as err:
        Utility.print_info(f'caught a timeout error: {err}')



#asyncio.run(asyncio_timeout_error())
asyncio.run(asyncio_timeout_class(2, 1))
asyncio.run(asyncio_timeout_class(2, 3))
asyncio.run(asyncio_timeout_at(2, 3))
asyncio.run(asyncio_timeout_at(2, 1))